iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 8
1
Modern Web

JavaScript基本功修煉系列 第 8

JavaScript基本功修練:Day8 - 算術運算子、賦值運算子、比較運算子

  • 分享至 

  • xImage
  •  

運算子,例如算術運算子+ - * /或者邏輯運算子&&||!,相信大家都看過了,所以這篇不會把所有內容都寫出來,而是針對某些部分作補充。例如作為新手,我雖然知道||&&的意思,但不太熟悉||的另一種用法(短路邏輯)。又例如++值值++這兩種寫法有什麼分別等等。

因此,接下來會用兩篇來整理自己重新看此篇表達式與運算子MDN文檔時不熟悉的部分,希望同時也對大家有所幫助。這篇主要記錄其中三個運算子:算術運算子賦值運算子比較運算子的知識。

表達式?陳述式?

在看運算子MDN文檔時,見到有一併提及表達式這個述語。為什麼明明在講運算子,但又把表達式混進來講呢?因為表達式有好幾種類型,而這些類型都是與運算子有關的。先看看簡單的定義:

  • 表達式(expression):
    意思是該段程式碼會產生一個值
    例如:x=73+4

  • 陳述式(statement):
    意思是該段程式碼會執行一個動作
    例如:宣告變數var a、函式、for迴圈、if else條件式。

在表達式的定義中,提到的x=73+4這兩個例子都有用到運算子。當中用到=去賦予一個值,也有用+去產生一個值。事實上,表達式有分多種類型,而當中不少都涉及到運算子,因為它們都會產生一個值,所以都是表達式類型之一。

表達式類型:

  • 算數 (Arithmetic)
  • 字串 (String)
  • 邏輯 (Logical)
  • 基本表達式 (Primary expressions)
  • 左值表達式 (Left-hand-side expressions)

運算子種類

運算子較常見的類型有:

  • 賦值運算子
    e.g: =+=
  • 比較運算子
    e.g: ==<>=
  • 算術運算子
    e.g: 加減乘除、++--**
  • 邏輯運算子
    e.g: &&||!
  • 字串運算子
    e.g: 'Hello' + 'World!'

詳細種類可看MDN
有一些特別類型,叫做一元、二元、三元(條件運算子)運算子,之後會再提及。

算術運算子

先從算術運算子的部分開始吧。

這部分的重點:

  • 重溫%**
  • 型別轉換
  • ++值值++ 的分別
  • 一元運算子 +-

較少看到的%**

除了一般的加減乘除,還有不時會見到,但自己又不太熟悉的%**,所以也要重溫一下。
%是指求餘,就是指兩個數值相除時得出的餘數:

console.log(5 % 2); //1
console.log(7 % 7); //0

//簡寫例子
let x = 15;
console.log(x %= 6); //即是 x = x % 6。回傳3

let y = 10;
console.log(y %= 0) //即是 y = y % 0。回傳NaN

**是指數的意思:

console.log(10 ** 2); //100
console.log(3 ** 0); //1

//簡寫例子
let a = 2;
console.log(a ** 2); //4
console.log(a ** 'hello'); //NaN

型別轉換

加減乘除時,如果涉及到字串,就會有自動轉換型別的情況。
例如這些例子:

這裏只有+,才會當作字串一樣去連接起來。在+的世界裏,如果其中一個運算數是字串,就會把不是字串的另一方轉成字串。針對這些情況,我們也看看以下比較特別的例子:

// 其中一方是字串的例子
'hello' + 123 // "hello123"
'123' + true // "123true"
'123' + null // "123null"
'123' + undefined // "123undefined"
'123' + [] // "123"
'123' + {} // "123[object Object]"

// 雙方都不是字串的例子

123 + [] // "123"

//因為{}是物件,會被轉成字串這個基本型別,再與123相加
123 + {}  // "123[object Object]"

//陣列以join(',')的方式被轉為基本型別,變成"1,2,3,4,5"
123 + [1,2,3,4,5] // "1231,2,3,4,5"

123 + null //123
123 + undefined //NaN
123 + true //124

物件的轉型規則是:除了date是用toString()去轉型,其他都會用valueOf(),如果valueOf()找不到基本型別值,就會用toString(),也是最常見的做法。

如下圖顯示,因為valueOf()找不到{}[]的基本型別值,所以會用toString()

值++++值的分別

算術運算子還包括一元運算子,例如遞加++和遞減--,但放在前面後放在後面又會有什麼分別呢?

let x = 10;
let y = 10;

console.log(x++) //10
console.log(++y) //11

console.log(x); //11
console.log(y); //11

由此可見,其實xy一樣都是11

當我們在變數前或後加上++,才會出現差別。

  • ++放在前面:
    回傳+1後的值

  • ++放在後面:
    回傳原本的數值

一元運算子

如果一元運算子+-後的值不是數字型別,就會用Number()把它轉成數字型別。如果是物件,就會用valueOf()去轉型,再按+-來取得數值:

let a = '1';
let b = '-1';
let c = 'abc';
let d = [];
let e = {};
let f = null;
let g = undefined;

console.log(+a,-a) // 1, -1
console.log(+b,-b) // -1, 1 
console.log(+c,-c) // NaN, NaN
console.log(+d,-d) // 0, -0
console.log(+e,-e) // NaN, NaN 
console.log(+f,-f) // 0, -0
console.log(+g,-g) // NaN, NaN

賦值運算子

這部分的重點:

  • a = b = c的寫法

在看賦值運算子這個部分時,看到a = b = c這種寫法。我學習JavaScript時很少會看到這樣寫(新手表示真的不知道QQ),所以也記錄一下。

let a = 10;
let b = 11;
let c = 12;

a = b = c; //相等於 a = (b = c);
console.log(a,b,c) //12, 12, 12

再複雜一點:

let a = 10;
let b = 11;
let c = 12;

a += b *= c
console.log(a,b,c) //142, 132, 12

//逐步拆解
// a += (b *= c)
// a += (b = b*c)
// a = a + (b = b*c)

除此之外,另一個不太熟的是解構賦值這個課題,但這個已經可以開新一篇去寫了,所以在之後會再談及~

比較運算子

這部分的重點:

  • ==相等比較與自動轉型
  • =====的差異

==相等比較與自動轉型

  • ==!= 相等比較(較寛鬆)
  • ===!== 全等比較(較嚴謹)

用較寛鬆的相等比較==!=之前,如果兩邊的值不是相同型別,就會先把某一個/全部值轉成同一型別,才會去做相等比較。我們可以重看之前文章中曾經引用過的這張圖表:

截圖自MDN

如果看懂這個表,以下例子就很易懂:

10 == '10' //true
'10' == true //false
10 == true //false
false == 0 //true 
true == 1 //true
true == 'true' //false
false == 'false' //false

以上的例子中,我曾經很直覺地想:「'true'不是true嗎?因為它不是空字串,為什麼不是等於true?」但注意,這裏並不是把'true'轉成布林值來作比較,而是把兩邊都轉成數字,才去做比較。所以,true會變成1true會變成NaN,所以這裏比較的是1 == NaN,答案就是false

[] == 0 //true
[] == '' //true
[''] == 0 //true
[0] == 0 //true
[0] == '' //false

[] == [] //false
{} == {} //false
//以上兩個例子,之前的文章有詳細解釋過

null == undefined //true

前5個例子中,按規則都需要把左邊的物件轉成基本值,意思是用toString()valueOf()的方法找出物件的基本值。

[]為例,valueOf()只返回原本的陣列,找不到基本值,所以我們要用toString()方法,並得出""。繼而我們再照字串比較數字的規則,把""轉成數字0,所以答案會是true

[] == []{} == {}因為記憶體地址不同,所以會是false,詳細解釋可看之前的文章
另外要注意的是nullundefined是相等的。

=====的差異

為了避免==會產生null == undefined這些情況,很多開發者都主張用===而非==,但我們也需要了解清楚它們之間的分別。You don't know JS 的作者在書中提及:

A very common misconception about these two operators is: "== checks values for equality and === checks both values and types for equality.” While that sounds nice and reasonable, it’s inaccurate. Countless well-respected JavaScript books and blogs have said exactly that, but unfortunately they’re all wrong.

The correct description is: "== allows coercion in the equality comparison and === disallows coercion.”

作者指出,如果說「==會檢查兩邊的值是否對等,而===會一併檢查兩邊的值和型別是否對等」,這個說法是錯的。正確的說法是「==會容許在比較時自動轉型,但===不容許在比較時自動轉型。」

In the first explanation, it seems obvious that === is doing more work than ==, because it has to also check the type. In the second explanation, == is the one doing more work because it has to follow through the steps of coercion if the types are different.

If you want coercion, use == loose equality, but if you don’t want coercion, use === strict equality.

The implication here then is that both == and === check the types of their operands. The difference is in how they respond if the types don’t match.

之後作者再強調,如果你想有自動轉型,就用==,否則就用========都會檢查型別,差異在於當型別不同時,=====的反應(即是會,還是不會幫它們自動轉型)。

有興趣可看看原文

總結

這篇主要是補充自己對表達式和陳述式算術運算子賦值運算子比較運算子課題上的知識。

明天會繼續承接運算子這個課題,針對講解邏輯運算子,例如||&&除了放在if else判斷式外的幾種用途,也會提及條件運算子條件 ? 值1 : 值2,感謝你的閱讀~

参考資料

JavaScript Addition Operator in Details
You Don't Know JS: Types & Grammar - Chapter 4. Coercion
重新認識 JavaScript: Day 07 「比較」與自動轉型的規則


上一篇
JavaScript基本功修練:Day7 - [] == [] 、[]==![]、{} == {}、{} ==!{} 的解釋
下一篇
JavaScript基本功修練:Day9 - 短路求值與條件運算子的應用
系列文
JavaScript基本功修煉31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言